-
Notifications
You must be signed in to change notification settings - Fork 2.1k
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Fix timeout issues #699 #700
Conversation
Don't merge for now, there's still a bug... I'll check for «bidsBackAll()» to clear the timeout. |
The issue lies in |
If I add:
and in bidsBackAdUnit():
I get:
And the second one which is launched as soon as the first auction finished:
So bidsBackAdUnit() works well (only one bid is back out of 4 bidders), but bidsBackAll() send a wrong answer. Currently working on a fix... That may fix this too: #496 |
The issue lies in the fact that |
|
@mkendall07 can your review my fixes please?
|
Can't fix for now... see comments in #699 To me |
this looks significant! did you A/B test impressions lost with and without this fix? |
@mercuryyy for now this fix isn't working at all. Still have issues with markComplete/removeComplete. I'm fixing it right now, and I'll remove some of the fixes I've done (some are useless in the new scenario I'm thinking about). But there's clearly a big issue here, when timeouts are not cleared or bidders don't answer. |
Sorry forgot to run the tests :-) But fix is working fine to me! So... back on tests! |
Why are adUnitCodes not passed in the requestBids() tests? Shouldn't it be set? |
@mkendall07 @matthewlane can you add the need review on this PR please? This fix is useful for all users of multiple calls to the callback. For example for users of infinite scroll. Thanks for your time. |
bidmanager.addOneTimeCallback(bidsBackHandler); | ||
auctionRunning = true; | ||
for (let i = 0; i < adUnitCodes.length; i++) { | ||
removePreviousBids(adUnitCodes[i]); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
As noted above we should avoid promoting adUnitCodes
as a primary data structure. The initial call to pbjs.requestBids
must supply adUnits
. Thereafter bids can be requested with adUnitCodes
but only for adUnits
already defined. In this block, for example, removePreviousBids
will only be invoked if there are adUnitCodes
, which may not be the case. See the 'No adUnits configured..' log message on line 552. adUnits
are assumed to be present, and if adUnitCodes
are present they are only used to filter adUnits
.
function bidsBackAll() { | ||
const requested = $$PREBID_GLOBAL$$._bidsRequested.map(bidSet => bidSet.bids.length).reduce(add, 0); | ||
const received = $$PREBID_GLOBAL$$._bidsReceived.length; | ||
function bidsBack(adUnitCodes) { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
In my testing this is called twice from addBidResponse
, once with the adUnitCode
of the bid response and once without arguments. In the latter case requested
and received
both evaluate to 0
which returns true
from this function which in turn invokes the callback earlier than expected and repeatedly for each call to bidmanager.addBidResponse
.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I'm in favor of combining the two bidsBack...
functions. If adUnitCodes
is not defined it could be derived from pbjs.adUnits
, and a .filter
function applied if an adUnitCode
is supplied to bidsBack
function.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Didn't see this issue with both zero values... But I'll look for a fix in received, which is the culprit in the case of two auctions (the second time the requested isn't filtered correctly).
@@ -71,10 +70,11 @@ function getBidSetForBidder(bidder) { | |||
exports.addBidResponse = function (adUnitCode, bid) { | |||
if (bid) { | |||
|
|||
const bidSet = getBidSetForBidder(bid.bidderCode, adUnitCode); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This could be expressed as
const { requestId, start } = getBidSetForBidder(bid.bidderCode, adUnitCode);
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Thanks!
@@ -9,6 +9,7 @@ var objectType_function = 'function'; | |||
var externalCallbackByAdUnitArr = []; | |||
var externalCallbackArr = []; | |||
var externalOneTimeCallback = null; | |||
var externalOneTimeCallbackTimer = false; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Ok for now, though I think we could consider a callbackHandler
object with callback
and timer
function properties and externalCallback...
array properties.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I've fixed this in #735
triggerAdUnitCallbacks(bid.adUnitCode); | ||
} | ||
|
||
if (bidsBackAll()) { | ||
if (bidsBack()) { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
As noted above this can evaluate to true when requested
and received
both evaluate to 0
.
@@ -222,6 +220,11 @@ exports.registerDefaultBidderSetting = function (bidderCode, defaultSetting) { | |||
}; | |||
|
|||
exports.executeCallback = function (timedOut) { | |||
// if there's still a timeout running, clear it now | |||
if (!timedOut && externalOneTimeCallbackTimer) { | |||
clearTimeout(externalOneTimeCallbackTimer); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Great catch.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Fixed in #735
@@ -37,6 +37,7 @@ var eventValidators = { | |||
|
|||
$$PREBID_GLOBAL$$._bidsRequested = []; | |||
$$PREBID_GLOBAL$$._bidsReceived = []; | |||
$$PREBID_GLOBAL$$._adUnitCodes = []; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
We should avoid another primary data structure here and consider adUnitCodes
to be derived from adUnits
. The codes are a shorthand way of refreshing bids for a previously defined group of adUnits
.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The issue here is that you can't know what adUnits are currently used in the current auction... But maybe that's because I've got rid of markComplete
.
} | ||
|
||
function removeComplete() { | ||
function removePreviousBids(adUnitCode) { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This raises the question of determining when bids are no longer valid. In removePreviousBids
here you remove all bids for a placement if that placement is included in the auction about to start. The code that this replaces marks bids complete for a placement if pbjs.renderAd
is called for that placement, which means we know the auction was run and the winning ad has been rendered. Those "complete" bids are only removed when a subsequent bid request is about to go out. This gives publishers as open a window as possible to call the reporting APIs for the most recent auction. Also, renderAd
is the only definitive knowledge Prebid has of the ad delivery. There is currently no Prebid handler for an ad render when filled by ad server demand.
As noted here and here and with this PR, the approach results in some unexpected "old" bids. However consider the case that there is nothing invalid about these bids necessarily, and including them in auctions may be desirable. For example, take a given placement auction where the highest bid is $5. As it happens the ad server fills with it's own demand. A subsequent request for bids for the placement returns a winner of $4. The "old" $5 bid is still a valid bid as far as the bidder is concerned. Why should we exclude the bid from the current outgoing auction?
We are already throwing out some bids we perhaps shouldn't. Consider a case where Prebid demand fills a placement. The winner was a $10 bid, with a $5 bid behind it. A second request for bids goes out with a winner of $2. In this case we currently markComplete
and removeComplete
so the $5 bid is thrown out and not considered in the second auction. Again, as far as the bidder is concerned, that is a valid $5 bid for the placement. Should we throw it out?
Thanks @protonate! I didn't expect such a review. You're right about a lot of points, I didn't think about the old bids that could be used again. I'll split this into multiple pulls, I think it's better that way. I'll refer to all your points one after the other in my future pulls. In #718 there's also the bug of |
Closing this PR because it will be split in multiple little ones |
Type of change
Description of change
If all bidders respond within the timeout, the callback is called again as the end of the timer, because it's not cleared. Hence the second auction ends too early.
Also, another bug fix: when an auction is running and another adUnit is sent to requestBids, the timeout was wrongly set as
cbTimeout
instead oftimeout
New fixes:
Other information
@mkendall07
#699